//! 任务抢占

// - bits 0-7  抢占计数(最大深度 256)
// - bits 8-11 irq 计数(理论最大为 1)
// - bit  12   nmi 计数(最大为 1)
//
//          PREEMPT_MASK:	0x000000ff
//          HARDIRQ_MASK:	0x00000f00
//              NMI_MASK:	0x00001000
//  PREEMPT_NEED_RESCHED:	0x80000000

use crate::{
    irq::irqflags::irqs_disabled,
    sched::{schedule::preempt_schedule, task::current_fast},
};

const PREEMPT_BITS: usize = 8;
const HARDIRQ_BITS: usize = 4;
const NMI_BITS: usize = 1;

const PREEMPT_SHIFT: usize = 0;
const HARDIRQ_SHIFT: usize = PREEMPT_SHIFT + PREEMPT_BITS;
const NMI_SHIFT: usize = HARDIRQ_SHIFT + HARDIRQ_BITS;

const fn preempt_mask(bits: usize) -> usize {
    (1 << bits) - 1
}

#[allow(unused)]
const PREEMPT_MASK: usize = preempt_mask(PREEMPT_BITS) << PREEMPT_SHIFT;
#[allow(unused)]
const HARDIRQ_MASK: usize = preempt_mask(HARDIRQ_BITS) << HARDIRQ_SHIFT;
#[allow(unused)]
const NMI_MASK: usize = preempt_mask(NMI_BITS) << NMI_SHIFT;

pub(crate) const PREEMPT_OFFSET: usize = 1 << PREEMPT_SHIFT;
pub(crate) const HARDIRQ_OFFSET: usize = 1 << HARDIRQ_SHIFT;
#[allow(unused)]
pub(crate) const NMI_OFFSET: usize = 1 << NMI_SHIFT;

#[inline(always)]
pub(crate) fn preempt_count_add(val: u32) {
    current_fast().preempt_add(val);
}

#[inline(always)]
pub(crate) fn preempt_count_sub(val: u32) {
    current_fast().preempt_sub(val);
}

/// 抢占激活不调度
#[inline(always)]
pub fn preempt_enable_no_resched() {
    preempt_count_sub(PREEMPT_OFFSET as u32);
}

/// 抢占激活
#[inline(always)]
pub fn preempt_enable() {
    preempt_count_sub(PREEMPT_OFFSET as u32);
    if preempt_count() == 0 && current_fast().need_resched() {
        preempt_schedule();
    }
}

/// 抢占禁止
#[inline(always)]
pub fn preempt_disable() {
    preempt_count_add(PREEMPT_OFFSET as u32);
}

/// 抢占计数
pub fn preempt_count() -> u32 {
    current_fast().preempt()
}

/// 在原子抢占关闭状态
///
/// 即此时不处于禁用抢占且无其他抢占计数状态(irq/nmi/nested preempt?)
#[inline(always)]
pub fn in_atomic_preempt_off() -> bool {
    preempt_count() != PREEMPT_OFFSET as u32
}

/// 可抢占
#[inline(always)]
pub fn preemptible() -> bool {
    preempt_count() == 0 && !irqs_disabled()
}
